#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
基于文件上传的语音转写服务
使用讯飞录音文件转写大模型API
"""

import base64
import hmac
import json
import os
import time
import random
import string
import requests
import urllib.parse
import datetime
import warnings
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 忽略SSL验证警告（生产环境建议开启验证）
warnings.filterwarnings("ignore", category=requests.packages.urllib3.exceptions.InsecureRequestWarning)

# 讯飞API基础配置
LFASR_HOST = "https://office-api-ist-dx.iflyaisol.com"
API_UPLOAD = "/v2/upload"
API_GET_RESULT = "/v2/getResult"


class FileTranscriptionService:
    """文件转写服务"""
    
    def __init__(self, app_id, access_key_id, access_key_secret, audio_file_path):
        self.app_id = app_id
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret
        self.audio_file_path = self._check_audio_path(audio_file_path)
        self.audio_duration = self._get_audio_duration()
        self.order_id = None
        self.signature_random = self._generate_random_str()

    def _check_audio_path(self, path):
        """检查音频文件路径"""
        if not os.path.exists(path):
            raise FileNotFoundError(f"音频文件不存在：{path}")
        return os.path.abspath(path)

    def _generate_random_str(self, length=16):
        """生成随机字符串"""
        return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

    def _get_local_time_with_tz(self):
        """生成带时区偏移的本地时间"""
        local_now = datetime.datetime.now()
        tz_offset = local_now.astimezone().strftime('%z')
        return f"{local_now.strftime('%Y-%m-%dT%H:%M:%S')}{tz_offset}"

    def _get_audio_duration(self):
        """获取音频时长（毫秒）"""
        # 简单的PCM文件：16kHz, 16bit, 单声道
        # 假设每16bit = 2字节，采样率16000Hz
        file_size = os.path.getsize(self.audio_file_path)
        # 时长(ms) = 文件大小(字节) / (采样率 * 每样本字节数) * 1000
        duration_ms = int(file_size / (16000 * 2) * 1000)
        logger.info(f"音频文件: {self.audio_file_path}, 大小: {file_size} 字节, 预估时长: {duration_ms} 毫秒")
        return duration_ms

    def generate_signature(self, params):
        """生成签名"""
        # 排除signature参数，按参数名自然排序
        sign_params = {k: v for k, v in params.items() if k != "signature"}
        sorted_params = sorted(sign_params.items(), key=lambda x: x[0])
        
        # 构建baseString
        base_parts = []
        for k, v in sorted_params:
            if v is not None and str(v).strip() != "":
                encoded_key = urllib.parse.quote(k, safe='')
                encoded_value = urllib.parse.quote(str(v), safe='')
                base_parts.append(f"{encoded_key}={encoded_value}")
        
        base_string = "&".join(base_parts)
        
        # HMAC-SHA1加密 + Base64编码
        hmac_obj = hmac.new(
            self.access_key_secret.encode("utf-8"),
            base_string.encode("utf-8"),
            digestmod="sha1"
        )
        signature = base64.b64encode(hmac_obj.digest()).decode("utf-8")
        return signature

    def upload_audio(self):
        """上传音频文件"""
        logger.info("开始上传音频文件...")
        
        # 准备参数
        audio_size = str(os.path.getsize(self.audio_file_path))
        audio_name = os.path.basename(self.audio_file_path)
        date_time = self._get_local_time_with_tz()
        
        url_params = {
            "appId": self.app_id,
            "accessKeyId": self.access_key_id,
            "dateTime": date_time,
            "signatureRandom": self.signature_random,
            "fileSize": audio_size,
            "fileName": audio_name,
            "language": "autodialect",
            "duration": str(self.audio_duration)
        }
        
        # 生成签名
        signature = self.generate_signature(url_params)
        
        # 构建请求头
        headers = {
            "Content-Type": "application/octet-stream",
            "signature": signature
        }
        
        # 构建URL
        encoded_params = []
        for k, v in url_params.items():
            encoded_key = urllib.parse.quote(k, safe='')
            encoded_v = urllib.parse.quote(str(v), safe='')
            encoded_params.append(f"{encoded_key}={encoded_v}")
        upload_url = f"{LFASR_HOST}{API_UPLOAD}?{'&'.join(encoded_params)}"
        
        # 读取并上传音频文件
        with open(self.audio_file_path, "rb") as f:
            audio_data = f.read()
        
        try:
            response = requests.post(
                url=upload_url,
                headers=headers,
                data=audio_data,
                timeout=30,
                verify=False
            )
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            raise Exception(f"上传请求失败：{str(e)}")
        
        # 解析响应
        try:
            result = json.loads(response.text)
            logger.info(f"上传响应: {json.dumps(result, ensure_ascii=False)}")
        except json.JSONDecodeError:
            raise Exception(f"API返回非JSON数据：{response.text}")
        
        # 检查错误
        if result.get("code") != "000000":
            raise Exception(f"上传失败：{result.get('descInfo', '未知错误')}")
        
        # 保存订单ID
        self.order_id = result["content"]["orderId"]
        logger.info(f"上传成功，订单ID: {self.order_id}")
        return result

    def get_transcribe_result(self, max_retry=100, retry_interval=5):
        """查询转写结果"""
        if not self.order_id:
            logger.info("未检测到订单ID，自动执行上传流程...")
            self.upload_audio()
        
        if not self.order_id:
            raise Exception("未获取到订单ID，无法查询转写结果")
        
        # 构建查询参数
        query_params = {
            "appId": self.app_id,
            "accessKeyId": self.access_key_id,
            "dateTime": self._get_local_time_with_tz(),
            "orderId": self.order_id,
            "signatureRandom": self.signature_random,
            "resultType": "transfer"
        }
        
        # 生成签名
        query_signature = self.generate_signature(query_params)
        query_headers = {
            "Content-Type": "application/json",
            "signature": query_signature
        }
        
        # 构建查询URL
        encoded_query_params = []
        for k, v in query_params.items():
            encoded_key = urllib.parse.quote(k, safe='')
            encoded_v = urllib.parse.quote(str(v), safe='')
            encoded_query_params.append(f"{encoded_key}={encoded_v}")
        query_url = f"{LFASR_HOST}{API_GET_RESULT}?{'&'.join(encoded_query_params)}"
        
        # 轮询查询
        retry_count = 0
        while retry_count < max_retry:
            try:
                response = requests.post(
                    url=query_url,
                    headers=query_headers,
                    data=json.dumps({}),
                    timeout=15,
                    verify=False
                )
                response.raise_for_status()
            except requests.exceptions.RequestException as e:
                raise Exception(f"查询请求失败：{str(e)}")
            
            try:
                result = json.loads(response.text)
                logger.info(f"查询响应: {json.dumps(result, ensure_ascii=False)}")
            except json.JSONDecodeError:
                raise Exception(f"查询响应非JSON数据：{response.text}")
            
            if result.get("code") != "000000":
                raise Exception(f"查询失败：{result.get('descInfo', '未知错误')}")
            
            # 检查转写状态
            process_status = result["content"]["orderInfo"]["status"]
            if process_status == 4:
                logger.info("转写完成！")
                return self._parse_order_result(result)
            elif process_status == -1:
                raise Exception(f"转写失败：状态码={process_status}")
            
            # 处理中，等待后重试
            retry_count += 1
            logger.info(f"转写处理中（已查询{retry_count}/{max_retry}次），{retry_interval}秒后重试...")
            time.sleep(retry_interval)
        
        raise Exception(f"查询超时：已重试{max_retry}次")

    def _parse_order_result(self, api_response):
        """解析转写结果"""
        try:
            order_result_str = api_response.get('content', {}).get('orderResult', '{}')
            
            # 处理转义字符
            import re
            cleaned_str = re.sub(r'\\\\', r'\\', order_result_str)
            
            # 解析JSON
            order_result = json.loads(cleaned_str)
            
            # 提取所有w字段
            w_values = []
            if 'lattice' in order_result:
                for lattice_item in order_result['lattice']:
                    if 'json_1best' in lattice_item:
                        json_1best = json.loads(lattice_item['json_1best'])
                        if 'st' in json_1best and 'rt' in json_1best['st']:
                            for rt_item in json_1best['st']['rt']:
                                if 'ws' in rt_item:
                                    for ws_item in rt_item['ws']:
                                        if 'cw' in ws_item:
                                            for cw_item in ws_item['cw']:
                                                if 'w' in cw_item:
                                                    w_values.append(cw_item['w'])
            
            # 拼接所有文字
            return ''.join(w_values)
            
        except Exception as e:
            logger.error(f"解析转写结果失败: {e}")
            return ""


if __name__ == "__main__":
    # 测试代码
    import json

    with open('config.json', 'r', encoding='utf-8') as f:
        config = json.load(f)
    
    service = FileTranscriptionService(
        app_id=config['app_id'],
        access_key_id=config['access_key_id'],
        access_key_secret=config['access_key_secret'],
        audio_file_path="test_1.pcm"
    )
    
    result = service.get_transcribe_result()
    print(f"\n转写结果: {result}")
